import TinyPng from "tinypngjs";
import { existsSync, statSync, readFileSync, copyFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync, rmdirSync } from "fs";
import { join, extname, dirname } from "path";
import nodePath from "path";
import { createHash } from "crypto";
import { getAllImgs } from "../utils/FileUtil";

var cacheHash: { [path: string]: string } = {};
var cacheHead = join(__dirname, '../../cache')
var cacheJsonUrl = join(cacheHead, 'cache.json');
var crtTime = 0;

export function clearAllCache() {
    let cacheUrl = join(cacheHead);
    delDir(cacheUrl);
    console.log('clear over');
}

function delDir(dirPath: string) {
    if (!existsSync(dirPath)) return;
    try {
        let stat = statSync(dirPath)
        if (stat.isDirectory()) {
            let files = readdirSync(dirPath);
            files.forEach(f => {
                let ifurl = join(dirPath, f)
                if (statSync(ifurl).isDirectory()) {
                    delDir(ifurl);
                } else {
                    unlinkSync(ifurl);
                }
            })
            rmdirSync(dirPath);
        } else if (stat.isFile()) {
            unlinkSync(dirPath);
        }
    } catch (e) {
        console.log(e)
    }
}

function readCacheData() {
    if (!existsSync(cacheJsonUrl)) {
        cacheHash = {};
        return;
    }
    cacheHash = JSON.parse(readFileSync(cacheJsonUrl).toString());
}

function hashFile(filePath: string, fileData?: Buffer) {
    let md5 = createHash('md5');
    if (!fileData) {
        fileData = readFileSync(filePath);
    }
    md5.update(fileData);
    let md5Str = md5.digest('hex');
    return md5Str;
}

function createDirSync(dirPath: string, callback?: () => void) {
    if (existsSync(dirPath)) {
        if (!!callback) {
            callback();
        }
    } else {
        createDirSync(dirname(dirPath), () => {
            mkdirSync(dirPath);
            if (!!callback) {
                callback();
            }
        });
    }
}

export enum ECompressResultCode {
    Error,
    Success,
    CopyFromCache,
}

/**
 * 压缩
 * @param path 需要压缩的路径，可以是文件夹路径，也可以是文件路径
 * @param out 压缩后的导出路径，可以是文件夹路径，也可以是文件路径
 */
export function compress(path: string | { path: string }[], out?: string | { path: string }[], onProgress?: (result: { filePath: string, code: ECompressResultCode }) => void): Promise<{ total: number, success: number }> {
    return new Promise<{ total: number, success: number }>(async (resolve, reject) => {
        const out_origin = out
        if (!out) out = path;
        let finishedNum = 0
        let successNum = 0

        crtTime = Date.now();
        readCacheData();

        if (typeof path == 'string') {
            if (!existsSync(path)) {
                console.log('no such file or dir!');
                onProgress && onProgress({ filePath: path, code: ECompressResultCode.Error })
                reject('no such file or dir')
                return;
            }
            try {
                if (statSync(path).isFile()) {
                    console.log('开始压缩图片==>', path);

                    let str = hashFile(path);
                    let cacheImgUrl = '';// join(cacheHead, str + extname(path));

                    let compressedName = cacheHash[str];
                    let hasCompressed = false;
                    for (let key in cacheHash) {
                        let temp = cacheHash[key];
                        if (temp == str) {
                            hasCompressed = true;// 之前被压缩过的
                            break;
                        }
                    }
                    if (compressedName) {
                        // 当前的源图片的内容hash，在缓存中有对应的压缩过的文件hash
                        cacheImgUrl = join(cacheHead, compressedName + extname(path));

                        // 如果要压缩的文件，在缓存中已经存在过了，且没改变，则将cache中缓存的图片直接拷贝到out中
                        if (typeof out == 'string' && existsSync(cacheImgUrl)) {
                            copyFileSync(cacheImgUrl, out);
                            // if (showComplete) complete();
                            console.log('use time:', Date.now() - crtTime + 'ms');
                            onProgress && onProgress({ filePath: path, code: ECompressResultCode.CopyFromCache })
                            resolve({ total: 1, success: 1 })
                            return;
                        }
                    } else if (hasCompressed) {
                        // 当前的图片已经是被压缩过的，且本地也有这个被压缩过的图片，则将cache中缓存的图片直接拷贝到out中
                        cacheImgUrl = join(cacheHead, str + extname(path));
                        if (typeof out == 'string' && existsSync(cacheImgUrl)) {
                            copyFileSync(cacheImgUrl, out);
                            onProgress && onProgress({ filePath: path, code: ECompressResultCode.CopyFromCache })
                            resolve({ total: 1, success: 1 })
                            console.log('use time:', Date.now() - crtTime + 'ms');
                            return;
                        }
                    }

                    await TinyPng.compressImg(path, out).then(() => {
                        if (typeof out == 'string') {
                            createDirSync(join(cacheHead));
                            let compressedName = hashFile(out);
                            cacheImgUrl = join(cacheHead, compressedName + extname(path));
                            copyFileSync(out, cacheImgUrl);
                            cacheHash[str] = compressedName;
                            saveJson()
                            onProgress && onProgress({ filePath: path, code: ECompressResultCode.Success })
                            resolve({ total: 1, success: 1 })
                        } else {
                            console.log('out path error');
                            onProgress && onProgress({ filePath: path, code: ECompressResultCode.Error })
                            reject();
                        }
                    }).catch(e => {
                        cacheHash[str] = '';
                        console.log(e);
                    })
                } else {
                    let files = [];
                    getAllImgs(path, files);
                    // let files = await TinyPng.getAllImg(path);
                    // console.log('!!~~\n', files);

                    for (let i = 0; i < files.length; i++) {
                        const url = files[i];
                        if(!out_origin){
                            out=url
                        }
                        const outI = (typeof out == 'string') ? out : out[i].path
                        const outUrl = statSync(outI).isFile() ? outI : `${nodePath.normalize(outI)}\\${nodePath.parse(url).base}`

                        compress(url, outUrl, onProgress).then((result) => {
                            successNum++;
                            finishedNum++
                            if (finishedNum == files.length) {
                                complete();
                                resolve({ total: files.length, success: successNum })
                            }
                        }).catch(() => {
                            finishedNum++
                            if (finishedNum == files.length) {
                                complete();
                                resolve({ total: files.length, success: successNum })
                            }
                        });
                    }

                    /* return;
                    await TinyPng.compress(path, out, (res, p) => {
                        console.log(res);
                        if (p == 1) {
                            complete()
                        }
                    }) */
                }
            } catch (e) {
                console.log(e);
                onProgress && onProgress({ filePath: path, code: ECompressResultCode.Error })
                reject();
            }
        } else {

            for (let i = 0; i < path.length; i++) {
                const url = path[i].path;
                if(!out_origin){
                    out=url
                }
                const outI = (typeof out == 'string') ? out : out[i].path
                const outUrl = statSync(outI).isFile() ? outI : `${nodePath.normalize(outI)}\\${nodePath.parse(url).base}`
                compress(url, outUrl, onProgress).then((result) => {
                    successNum++;
                    finishedNum++
                    if (finishedNum == path.length) {
                        complete();
                        resolve({ total: path.length, success: successNum })
                    }
                }).catch(() => {
                    finishedNum++
                    if (finishedNum == path.length) {
                        complete();
                        resolve({ total: path.length, success: successNum })
                    }
                });
            }

        }
    })

}

function saveJson() {
    let str = JSON.stringify(cacheHash);
    writeFileSync(cacheJsonUrl, str);

    console.log('use time:', Date.now() - crtTime + 'ms');

}

function complete() {

    console.log('+------------------------+');
    console.log('|    compress finish!    |');
    console.log('+------------------------+\n');

}